package org.example.facade;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.MenuItem;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.*;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.transform.Affine;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import lombok.extern.slf4j.Slf4j;
import org.example.business_logic.component.Baffle;
import org.example.business_logic.component.Ball;
import org.example.physics_engine.AbstractPythicsWorld;
import org.example.physics_engine.BasicSettings;
import org.example.physics_engine.body.PhysicsBody;
import org.example.physics_engine.geometry.AxisAlignBoundingBox;
import org.example.physics_engine.body.Transform;
import org.example.physics_engine.geometry.Vector2d;
import org.example.physics_engine.geometry.shapes.AbstractShape;
import org.example.physics_engine.geometry.shapes.Polygon;
import org.example.facade.component.*;
import org.example.facade.panel.DefaultPanelRenderer;
import org.example.facade.panel.GizmoPhysicsBody;
import org.example.facade.panel.Utility;

import java.io.File;
import java.net.URL;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.ResourceBundle;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

@Slf4j
public class PlayPanel extends Application implements Initializable {


    //渲染弹球游戏界面的canvas
    @FXML
    Canvas gizmoCanvas;
    //游戏组件的面板（球，方，角，管道...）
    @FXML
    GridPane gizmoGridPane;
    //操作游戏组件的面板（删除，缩放，旋转...）
    @FXML
    HBox upperHBox;
    @FXML
    HBox lowerHBox;
    @FXML
    javafx.scene.shape.Rectangle gizmoOutlineRectangle;
    @FXML
    MenuItem menuItemLoad;
    @FXML
    MenuItem menuItemSave;
    @FXML
    MenuItem menuItemClear;
    @FXML
    MenuItem menuItemAbout;
    @FXML
    ImageView previewImageView;
    @FXML
    AnchorPane anchorPane;

    //区别于生产模式？
    private static final boolean DEV_MODE = false;

    private GridWorld gridWorld;

    //是否处于编辑模式
    private boolean inDesign = true;

    //选中当前组件
    private GizmoPhysicsBody selectedBody;
    private OptionHandler optionHandler;

    //拖拽传参的key
    private static final DataFormat GIZMO_TYPE_DATA = new DataFormat("gizmo");

    private static Vector2d preferredSize;

    private Stage primaryStage;

    private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();

    private final ScheduledFuture<?>[] scheduledFuture = new ScheduledFuture<?>[1];

    private Runnable r;

    //每个Component_Draggable是所有的VBox（UI）和其对应的Component_Type（里面写了其id、对应的实体创造函数和
    private static final Component_Draggable[] gizmos = {
            new Component_Draggable("icons/earth.png", "Ball", Component_Type.BALL),
            new Component_Draggable("icons/blackhole.png", "Black hole", Component_Type.HOLE),
            new Component_Draggable("icons/triangle.png", "Triangle", Component_Type.TRIANGLE),
            new Component_Draggable("icons/circle1.png", "Circle", Component_Type.CIRCLE),
            new Component_Draggable("icons/recentangle1.png", "Rectangle", Component_Type.RECTANGLE),
            new Component_Draggable("icons/orbit.png", "Orbit", Component_Type.ORBIT),
            new Component_Draggable("icons/orbit_curved.png", "Curved orbit", Component_Type.CURVED_ORBIT),
            new Component_Draggable("icons/left_flipper.png", "Left flipper", Component_Type.LEFT_BARRIER),
            new Component_Draggable("icons/right_flipper.png", "Right flipper", Component_Type.RIGHT_BARRIER),
    };

    private static final Command_component[] gizmoOps = {
            new Command_component("icons/delete1.png", "Delete", Gizmo_command.REMOVE),
            new Command_component("icons/zoomout.png", "Zoom out", Gizmo_command.ZOOM_OUT),
            new Command_component("icons/zoomin.png", "Zoom in", Gizmo_command.ZOOM_IN),
            new Command_component("icons/rotate1.png", "Rotate", Gizmo_command.ROTATE_RIGHT),
            
//            new Command_component("icons/move_up.png", "move up", Gizmo_common.MOVE_UP),
//            new Command_component("icons/move_right.png", "move right", Gizmo_common.MOVE_RIGHT),
//            new Command_component("icons/move_down.png", "move down", Gizmo_common.MOVE_DOWN),
//            new Command_component("icons/move_left.png", "move left", Gizmo_common.MOVE_LEFT),
    };

    //
    private static final ImgLabel[] gameOps = {
            new ImgLabel("icons/start.png", "Start"),
            new ImgLabel("icons/design1.png", "Design"),
    };

    @Override
    public void start(Stage primaryStage) throws Exception {
        Parent root = FXMLLoader.load(getClass().getClassLoader().getResource("main.fxml"));
        primaryStage.setOnCloseRequest(e -> {
            System.exit(0);
        });
        primaryStage.getIcons().add(new Image("icons/earth.png"));
        primaryStage.setTitle("Gizmo");
        primaryStage.setScene(new Scene(root));
        primaryStage.show();
        primaryStage.setResizable(false);
        primaryStage.sizeToScene();
        this.primaryStage = primaryStage;

        root.requestFocus();
    }

    private void initGizmoGridPane() {
        System.out.println("initGizmoGridPane");
        for (int i = 0; i < gizmos.length; i++) {
            Component_Draggable gizmo = gizmos[i];
            gizmoGridPane.add(gizmo.createVBox(), i % 3, i / 3); // 3行3列
            // 添加拖拽事件监听器
            // 拖拽传参为gizmo的类型
            int finalI = i;
            gizmo.getImageWrapper().setOnDragDetected(event -> {
                if (!DEV_MODE && !inDesign) {
                    return;
                }
                Dragboard db = gizmo.getImageView().startDragAndDrop(TransferMode.ANY);
                db.setDragView(gizmo.getImageView().getImage());
                ClipboardContent content = new ClipboardContent();
                content.put(GIZMO_TYPE_DATA, finalI);
                db.setContent(content);
                event.consume();
            });
        }
    }

    private void bindGizmoOp(Gizmo_command command) {
        if (selectedBody == null || !inDesign) {
            return;
        }
        try {
            boolean success = optionHandler.handleCommand(command, selectedBody);
            if (success) {
                if (command == Gizmo_command.REMOVE) {
                    selectedBody = null;
                }
                highlightSelectedBody();
                drawGizmo(gizmoCanvas.getGraphicsContext2D());
            }
        } catch (Exception e) {
            Toast.makeText(primaryStage, "操作物件失败: " + e.getMessage(), 2000, 500, 500);
            log.error("操作物件失败: {}", e.getMessage());
        }
    }

    private void initGizmoOp() {
        System.out.println("initGizmoOP");
        // 初始物件操作
        for (Command_component gizmoOp : gizmoOps) {
            gizmoOp.createVBox().setMaxWidth(70);
            gizmoOp.getImageWrapper().setOnMouseClicked(event -> {
                bindGizmoOp(gizmoOp.getGizmoCommand());
            });
        }
    }

    private void initGameOpHBox() {
        //r是一个任务，初始化他
        r = () -> {
            try {
                gridWorld.updateStep();
                //主线程更新画布
                Platform.runLater(() -> drawGizmo(gizmoCanvas.getGraphicsContext2D()));
            } catch (Exception e) {
                Platform.runLater(() -> {
                    designGame();
                    Toast.makeText(primaryStage, e.getMessage(), 2000, 500, 500);
                    log.error("游戏发生异常: {}", e.getMessage());
                });
            }
        };

        // 开始游戏
        ImgLabel play = gameOps[0];
        play.createVBox();
        play.getImageWrapper().setOnMouseClicked(event -> {
            startGame();
        });

        // 暂停游戏（设计模式）
        ImgLabel design = gameOps[1];
        design.createVBox();
        design.getImageWrapper().setOnMouseClicked(event -> {
            designGame();
        });
    }


    private void startGame() {
        if (!inDesign) {
            return;
        }
        selectedBody = null;
        highlightSelectedBody();
        inDesign = false;
        gridWorld.snapshot();
        //1000/60个毫秒执行一次r，即更新一次
        scheduledFuture[0] = scheduledExecutorService.scheduleAtFixedRate(r, 0, (long) (1000.0 / BasicSettings.TICKS_PER_SECOND),
                TimeUnit.MILLISECONDS);
    }

    private void designGame() {
        if (inDesign) {
            return;
        }
        selectedBody = null;
        highlightSelectedBody();
        inDesign = true;
        scheduledFuture[0].cancel(true);

        try {
            gridWorld.restore();
        } catch (RuntimeException e) {
            Toast.makeText(primaryStage, e.getMessage(), 12000, 500, 500);
            log.error("恢复游戏失败: {}", e.getMessage());
        }
        drawGizmo(gizmoCanvas.getGraphicsContext2D());
    }

    /**
     * 初始化游戏操作按钮
     */
    private void initGizmoOpHBox() {
        System.out.println("initGizmoOpHBox");
        // 初始物件操作
        initGizmoOp();
        // 初始化游戏操作（开始，设计）
        initGameOpHBox();

        for (int i = 0; i < 4; i++) {
            upperHBox.getChildren().add(gizmoOps[i].getVBox());
        }

        upperHBox.setSpacing(20);
        lowerHBox.getChildren().add(gameOps[0].getVBox());

        BorderPane borderPane = new BorderPane();
        //BorderPane.setAlignment(gizmoOps[4].getImageWrapper(), Pos.CENTER);
        //BorderPane.setAlignment(gizmoOps[5].getImageWrapper(), Pos.CENTER_RIGHT);
        //BorderPane.setAlignment(gizmoOps[6].getImageWrapper(), Pos.CENTER);
        //BorderPane.setAlignment(gizmoOps[7].getImageWrapper(), Pos.CENTER_LEFT);

        //borderPane.setTop(gizmoOps[4].getImageWrapper());
        //borderPane.setRight(gizmoOps[5].getImageWrapper());
       // borderPane.setBottom(gizmoOps[6].getImageWrapper());
       // borderPane.setLeft(gizmoOps[7].getImageWrapper());

        Pane pane = getPane();
        borderPane.setCenter(pane);
        lowerHBox.getChildren().add(borderPane);

        lowerHBox.getChildren().add(gameOps[1].getVBox());
    }

    private static Pane getPane() {
        Pane pane = new Pane();
        pane.setPrefWidth(60);
        pane.setPrefHeight(60);
        return pane;
    }

    /**
     * 初始化世界
     */
    private void initWorld() {
        System.out.println("initWorld");
        double worldWidth = gizmoCanvas.getWidth();
        double worldHeight = gizmoCanvas.getHeight();
        gridWorld = new GridWorld(AbstractPythicsWorld.GRAVITY, (int) worldWidth, (int) worldHeight, 30);
        preferredSize = new Vector2d(gridWorld.getGridSize(), gridWorld.getGridSize());
        optionHandler = new OptionHandler(gridWorld);
    }

    private void initMenuItem() {
        FileChooser fileChooser = new FileChooser();
        fileChooser.setTitle("选择文件");
        fileChooser.getExtensionFilters().addAll(
                new FileChooser.ExtensionFilter("Gizmo", "*.json"),
                new FileChooser.ExtensionFilter("All Files", "*.*"));
        final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss");

        menuItemLoad.setOnAction(event -> {
            if (!inDesign) return;
            fileChooser.setInitialDirectory(new File("."));
            File file = fileChooser.showOpenDialog(primaryStage);
            if (file != null) {
                try {
                    gridWorld.restore(file);
                    drawGizmo(gizmoCanvas.getGraphicsContext2D());
                } catch (Exception e) {
                    Toast.makeText(primaryStage, "加载文件失败: " + e.getMessage(), 1500, 200, 200);
                    log.error("加载文件失败: {}", e.getMessage());
                }
            }
        });

        menuItemSave.setOnAction(event -> {
            if (!inDesign) return;
            // set current time as filename
            String time = LocalDateTime.now().format(formatter);

            fileChooser.setInitialFileName("gizmo" + time + ".json");
            File file = fileChooser.showSaveDialog(primaryStage);
            if (file != null) {
                try {
                    gridWorld.snapshot(file);
                } catch (Exception e) {
                    Toast.makeText(primaryStage, "保存文件失败: " + e.getMessage(), 1500, 200, 200);
                    log.error("保存文件失败: {}", e.getMessage());
                }
            }
        });

        menuItemClear.setOnAction(event -> {
            //判断处于设计模式
            if (inDesign) {
                gridWorld.removeAllBodies();
                gridWorld.initBoundary();
                drawGizmo(gizmoCanvas.getGraphicsContext2D());
            }
        });

    }

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        initWorld();
        // 初始化界面
        initGizmoGridPane();
        initGizmoOpHBox();
        initCanvas();
        initMenuItem();

        anchorPane.setOnMouseClicked(event -> {
            gizmoCanvas.requestFocus();
        });
        anchorPane.setOnKeyPressed(event -> {
            switch (event.getCode()) {
                case LEFT:
                    gridWorld.flipperUp(Baffle.Direction.LEFT);
                    break;
                case RIGHT:
                    gridWorld.flipperUp(Baffle.Direction.RIGHT);
                    break;
                case F1:
                    isDebugMode = !isDebugMode;
                    break;
                case DELETE:
                    bindGizmoOp(Gizmo_command.REMOVE);
                    break;
                case CONTROL:
                    bindGizmoOp(Gizmo_command.ROTATE_RIGHT);
                    break;
                case ENTER:
                    if (inDesign) {
                        startGame();
                    } else {
                        designGame();
                    }
                    break;
            }
        });

        anchorPane.setOnKeyReleased(event -> {
            switch (event.getCode()) {
                case LEFT:
                    gridWorld.flipperDown(Baffle.Direction.LEFT);
                    break;
                case RIGHT:
                    gridWorld.flipperDown(Baffle.Direction.RIGHT);
                    break;
            }
        });

        anchorPane.setOnScroll(event -> {
            double deltaY = event.getDeltaY();
            if (deltaY > 0) {
                bindGizmoOp(Gizmo_command.ZOOM_OUT);
            } else if (deltaY < 0) {
                bindGizmoOp(Gizmo_command.ZOOM_IN);
            }
        });
    }

    // ------------canvas-----------------

    /**
     * 高亮当前选中物体
     */
    protected void highlightSelectedBody() {
        if (selectedBody == null) {
            gizmoOutlineRectangle.setVisible(false);
            return;
        }
        AxisAlignBoundingBox aabb = selectedBody.getShape().createAABB();
        Utility.padToSquare(aabb);
        gizmoOutlineRectangle.setX(aabb.minX);
        gizmoOutlineRectangle.setY(gridWorld.boundaryAABB.maxY - aabb.maxY);
        gizmoOutlineRectangle.setWidth(aabb.maxX - aabb.minX);
        gizmoOutlineRectangle.setHeight(aabb.maxY - aabb.minY);
        gizmoOutlineRectangle.setVisible(true);
    }

    private void initCanvas() {
        // 设置坐标系转换
        GraphicsContext gc = gizmoCanvas.getGraphicsContext2D();
        Affine affine = new Affine();
        // 正常设置
        affine.appendScale(1, -1);
        affine.appendTranslation(0, -gizmoCanvas.getHeight());
        gc.setTransform(affine);

        // 增加拖拽监听器
        Canvas target = gizmoCanvas;
        target.setOnMouseClicked(event -> {
            target.requestFocus();
            if (event.getButton() == MouseButton.PRIMARY) {
                double x = event.getX();
                double y = gridWorld.boundaryAABB.maxY - event.getY();
                // 获取当前index
                int[] index = gridWorld.getGridIndex(x, y);
                if (index != null) {
                    selectedBody = gridWorld.gizmoGridBodies[index[0]][index[1]];
                    highlightSelectedBody();
                }
            }
        });
        // 拖到画布上时显示物件预览/能否拖拽
        target.setOnDragOver(event -> {
            if (event.getGestureSource() != target) {

                Dragboard db = event.getDragboard();
                int gizmoIndex = (int) db.getContent(GIZMO_TYPE_DATA);
                Component_Draggable gizmo = gizmos[gizmoIndex];

                // 显示预览图片
                double x = event.getX();
                double y = event.getY();
                System.out.println("x="+x);
                System.out.println("y="+y);
                int[] index = gridWorld.getGridIndex(x, y);
                System.out.println(index);
                if (index != null) {
                    System.out.println("ImageView");
                    PhysicsBody[][] gridBodies = gridWorld.gizmoGridBodies;
                    int i = index[0];
                    int j = gridBodies[0].length - index[1] - 1;
                    if (gridBodies[i][j] == null) {
                        previewImageView.setVisible(true);
                        previewImageView.setImage(gizmo.getImage());
                        previewImageView.setLayoutX(index[0] * gridWorld.getGridSize());
                        previewImageView.setLayoutY(index[1] * gridWorld.getGridSize());
                        event.acceptTransferModes(TransferMode.COPY);
                    }
                }
            }
            event.consume();
        });
        target.setOnDragExited(event -> {
            previewImageView.setVisible(false);
            event.consume();
        });
        target.setOnDragDropped(event -> {
            if (!DEV_MODE && !inDesign) {
                return;
            }
            Dragboard db = event.getDragboard();
            int gizmoIndex = (int) db.getContent(GIZMO_TYPE_DATA);
            Component_Draggable gizmo = gizmos[gizmoIndex];

            int gridSize = gridWorld.getGridSize();
            Vector2d transformedCenter = new Vector2d(event.getX(), gridWorld.boundaryAABB.maxY - event.getY());
            // 以鼠标所在的点创建一个格子大小的AABB
            AxisAlignBoundingBox centerAABB = new AxisAlignBoundingBox(-gridSize / 2.0, -gridSize / 2.0, gridSize / 2.0, gridSize / 2.0);
            centerAABB.translate(transformedCenter);
            // 移到边界内
            Vector2d offsetToBoundary = Utility.offsetToBoundary(centerAABB, gridWorld.boundaryAABB);
            transformedCenter.add(offsetToBoundary);
            centerAABB.translate(offsetToBoundary);
            // 对齐到网格
            Vector2d snapped = Utility.snapToGrid(centerAABB, gridSize, gridSize);
            transformedCenter.add(snapped);
            GizmoPhysicsBody physicsBody = gizmo.createPhysicsBody(preferredSize, transformedCenter);
            try {
                optionHandler.addGizmo(physicsBody);
            } catch (Exception e) {
                Toast.makeText(primaryStage, e.getMessage(), 1500, 200, 200);
            }

            drawGizmo(gc);
            previewImageView.setVisible(false);
            event.setDropCompleted(true);
            event.consume();
        });

        drawGizmo(gc);
    }

    private void clearCanvas(GraphicsContext gc) {
        gc.clearRect(0, 0, gizmoCanvas.getWidth(), gizmoCanvas.getHeight());
    }

    private void drawGrid(GraphicsContext gc) {
        System.out.println("drawGrid");
        int gridSize = gridWorld.getGridSize();
        gc.setStroke(Color.GRAY);
        gc.setLineWidth(1);
        for (int i = 0; i < gizmoCanvas.getWidth(); i += gridSize) {
            gc.strokeLine(i, 0, i, gizmoCanvas.getHeight());
        }
        for (int i = 0; i < gizmoCanvas.getHeight(); i += gridSize) {
            gc.strokeLine(0, i, gizmoCanvas.getWidth(), i);
        }
    }

    private static final DefaultPanelRenderer canvasRenderer = DefaultPanelRenderer.INSTANCE;

    private void drawGizmo(GraphicsContext gc) {
        clearCanvas(gc);
        drawGrid(gc);

        List<GizmoPhysicsBody> bodies = gridWorld.getBodies();
        for (GizmoPhysicsBody physicsBody : bodies) {
            if (physicsBody instanceof GizmoPhysicsBody && physicsBody!=null) {
                physicsBody.drawToCanvas(gc);
                if (isDebugMode) {
                    if (physicsBody.getShape() instanceof Ball) {
                        Vector2d linearVelocity = physicsBody.getLinearVelocity().copy();
                        Vector2d normalized = linearVelocity.copy().right().getNormalized();
                        double angularVelocity = physicsBody.getAngularVelocity() * 20;
                        Transform transform = physicsBody.getShape().getTransform();
                        gc.setStroke(Color.GREEN);
                        gc.strokeLine(transform.getX(), transform.getY(), transform.getX() + linearVelocity.x, transform.getY() + linearVelocity.y);
                        gc.setStroke(Color.RED);
                        gc.strokeLine(transform.getX(), transform.getY(), transform.getX() + normalized.x * angularVelocity, transform.getY() + normalized.y * angularVelocity);
                    } else {
                        AbstractShape shape = physicsBody.getShape();
                        if (shape instanceof Polygon) {
                            Polygon shape1 = (Polygon) shape;
                            Vector2d[] normals = shape1.getNormalVectors();
                            Transform transform = physicsBody.getShape().getTransform();
                            for (Vector2d normal : normals) {
                                Vector2d multiply = normal.copy().multiplyNum(30);
                                Vector2d transformed = transform.getTransformed(multiply);
                                gc.setStroke(Color.YELLOW);
                                gc.strokeLine(transform.getX(), transform.getY(), transformed.x, transformed.y);
                            }
                        }
                    }
                }
            } else {
                canvasRenderer.drawToCanvas(gc, physicsBody);
            }
        }
    }

    private boolean isDebugMode = false;



}
